// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camera;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.hardware.camera2.CameraAccessException;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.provider.Settings;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import android.os.Looper;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugins.camera.CameraPermissions.PermissionsRegistry;
import io.flutter.plugins.camera.features.resolution.ResolutionPreset;
import io.flutter.view.TextureRegistry;
import java.util.HashMap;
import java.util.Map;

final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
  private final Activity activity;
  private final BinaryMessenger messenger;
  private final CameraPermissions cameraPermissions;
  private final PermissionsRegistry permissionsRegistry;
  private final TextureRegistry textureRegistry;
  private final MethodChannel methodChannel;
  private final EventChannel imageStreamChannel;
  private Result _result;

  private CameraService cameraService;
  private CameraConnection connection;

  MethodCallHandlerImpl(
      Activity activity,
      BinaryMessenger messenger,
      CameraPermissions cameraPermissions,
      PermissionsRegistry permissionsAdder,
      TextureRegistry textureRegistry) {
    this.activity = activity;
    this.messenger = messenger;
    this.cameraPermissions = cameraPermissions;
    this.permissionsRegistry = permissionsAdder;
    this.textureRegistry = textureRegistry;

    methodChannel = new MethodChannel(messenger, "plugins.flutter.io/camera_android");
    imageStreamChannel =
        new EventChannel(messenger, "plugins.flutter.io/camera_android/imageStream");
    methodChannel.setMethodCallHandler(this);
  }

  public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
    if(requestCode == 3052) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        _result.success(Settings.canDrawOverlays(activity));
      } else {
        _result.success(false);
      }
      _result = null;
      return true;
    }
    return false;
  }

  @Override
  public void onMethodCall(MethodCall call, final Result result) {
    switch (call.method) {
      case "availableCameras":
        try {
          result.success(CameraUtils.getAvailableCameras(activity));
        } catch (Exception e) {
          CameraService.handleException(e, result);
        }
        break;
      case "overlayPermission":
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
          _result = result;
          Intent intent = new Intent(
                  Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                  Uri.parse("package:" + activity.getPackageName()));
          activity.startActivityForResult(intent, 3052);
        } else {
          result.success(false);
        }
        break;
      case "hasOverlayPermission" :
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
          result.success(Settings.canDrawOverlays(activity));
        } else {
          result.success(false);
        }
        break;
      case "create": {
        if (cameraService != null) {
          if (connection != null) {
            connection.call = call;
            connection.result = result;
            connection.connectCamera();
          } else {
            result.success(null);
          }
          return;
        }
        try {
          connection = new CameraConnection();
          connection.call = call;
          connection.result = result;
          Intent intent = new Intent(activity, CameraService.class);
          boolean b = activity.bindService(intent, connection, Context.BIND_AUTO_CREATE);
          if(!b) {
            result.success(null);
          }
        } catch (SecurityException e) {
          CameraService.handleException(e, result);
        }
        break;
      }
      case "prepareForVideoRecording": {
        // This optimization is not required for Android.
        result.success(null);
        break;
      }
      case "setBrightnessOffset": { //改变当前屏幕亮度
        double brightness = call.argument("offset");
        Window window = activity.getWindow();
        WindowManager.LayoutParams lp = window.getAttributes();
        lp.screenBrightness = (float) brightness;
        window.setAttributes(lp);
        result.success(brightness);
        break;
      }
      case "keepOn":
        boolean on = call.argument("on");
        if (on) {
          activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }
        else{
          activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }
        result.success(null);
        break;
      default:
        if (cameraService != null) {
          cameraService.onMethodCall(call, result, activity, imageStreamChannel);
        } else {
          Log.e("camera", "onMethodCall: cameraService is null");
        }

        if ("dispose".equals(call.method)) {
          if(cameraService != null) {
            activity.unbindService(connection);
          }
          cameraService = null;
        }
        break;
    }
  }

  private void instantiateCamera(MethodCall call, MethodChannel.Result result) throws CameraAccessException {
    String cameraName = call.argument("cameraName");
    String preset = call.argument("resolutionPreset");
    boolean enableAudio = call.argument("enableAudio");
    int width = call.argument("width");
    int style = call.argument("style");

    TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture =
            textureRegistry.createSurfaceTexture();
    DartMessenger dartMessenger =
            new DartMessenger(
                    messenger, flutterSurfaceTexture.id(), new Handler(Looper.getMainLooper()));
    CameraProperties cameraProperties =
            new CameraPropertiesImpl(cameraName, CameraUtils.getCameraManager(activity));
    ResolutionPreset resolutionPreset = ResolutionPreset.valueOf(preset);

    cameraService.setOverlayWidth(width, style);
    cameraService.closeCamera();
    cameraService.create(
            activity,
            flutterSurfaceTexture,
            dartMessenger,
            cameraProperties,
            resolutionPreset,
            enableAudio);

    Map<String, Object> reply = new HashMap<>();
    reply.put("cameraId", flutterSurfaceTexture.id());
    result.success(reply);
  }

  void stopListening() {
    methodChannel.setMethodCallHandler(null);
    try {
      if (connection != null) activity.unbindService(connection);
    } catch (IllegalArgumentException e){
      e.printStackTrace();
    }
  }

  private class CameraConnection implements ServiceConnection {

    MethodCall call;
    MethodChannel.Result result;

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
      cameraService = ((CameraService.CameraBinder)iBinder).get();
      connectCamera();
    }

    private void connectCamera() {
      cameraPermissions.requestPermissions(
              activity,
              permissionsRegistry,
              call.argument("enableAudio"),
              (String errCode, String errDesc) -> {
                if (errCode == null) {
                  try {
                    instantiateCamera(call, result);
                  } catch (Exception e) {
                    CameraService.handleException(e, result);
                  }
                } else {
                  result.error(errCode, errDesc, null);
                }
                call = null;
                result = null;
              });
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
      cameraService = null;
    }
  };

}
